#include "definicionesParser.h"
#include "./termSelection/termSelection.h"

char caracteresSeparadores[] = {' ', '\n', '.', ',', ';', '\t', '!', '?', '"',
'(', ')', '=', '#', ':', '\'', '&', '%', '*', '+', '$', '/', '\\'};
#define CaracteresSeparadoresLength 22
char CaracteresAdmitidosPalabra[] = {'-'};
#define CaracteresAdmitidosPalabraLength 1

char* repositorioNombre;
unsigned int rank;
char* pathCarpetaDatos;

bool strExisteCadena(const char* cadena, char caracter)
{
	int largo = strlen(cadena);
	
	for (int i = 0; i < largo; i++)
	{
		if (cadena[i] == caracter) return true;
	}
	
	return false;
}

void palabrasListaDestruir(lista_t* listaPalabra)
{
	lista_destruir(listaPalabra, free);
}

int compElementoAbbMerge(elementoAbbMerge_t* A, elementoAbbMerge_t* B)
{
	int cmp = strcmp(A->palabra, B->palabra);
	if (cmp != 0) return -cmp;
	
	if (A->documentoIndex > B->documentoIndex) return -1;
	if (A->documentoIndex < B->documentoIndex) return 1;
	
	return 0;
}

int compElementoMatriz(elementoMatriz_t* A, elementoMatriz_t* B)
{
	if (A->columna > B->columna) return -1;
	if (A->columna < B->columna) return 1;
	
	// if (A->columna == B->columna) 	no es necesaria esta linea
	if (A->fila > B->fila) return -1;
	if (A->fila < B->fila) return 1;
	
	return 0;
}

void saltarLineasArchivo(unsigned int cantLineas,FILE* fd){
	char buffer[255];
	for (int i = 0; i < cantLineas; i++){
		fgets(buffer, sizeof(buffer),fd);
	}
}

elementoMatriz_t* leerElementoMatriz(FILE* fd)
{

	char palabra[MaxCaracteresPorPalabra];
	bool agregarCaracter = true;
	unsigned long index = 0;

	int estado = 0;		// 0 Documento, 1 Palabra, 2 Valor
	elementoMatriz_t* elemento = malloc(sizeof(elementoMatriz_t));
	if (elemento == NULL) return NULL;

	char c = fgetc(fd);
	if (c == EOF)
	{
		free(elemento);
		return NULL;
	}

	while(1)
	{
		if (c == CaracterSeparadorMatriz || c == '\n') agregarCaracter = false;

		if (agregarCaracter == false)
		{
			palabra[index] = '\0';
			if (estado == 0)
			{
				elemento->fila = (unsigned long) atol(palabra);
				estado++;
			} else if (estado == 1)
			{
				elemento->columna = (unsigned long) atol(palabra);
				estado++;
			} else if (estado == 2)
			{
				elemento->valor = (double) atof(palabra);
				estado++;
			}
			agregarCaracter = true;
			index = 0;
		} else {
			palabra[index] = c;
			index++;
		}

		if (c == '\n') break;
		c = fgetc ( fd );
	}

	return elemento;
}

void elementoAbbMerge_destruir(elementoAbbMerge_t* elemento)
{
	free(elemento->palabra);
	free(elemento);
}

abb_disk_t* abb_disk_crear( abb_comparar_clave_t cmp, abb_destruir_dato_t destruir_dato )
{
	abb_disk_t* abb_disk = malloc(sizeof(abb_disk_t));
	if (abb_disk == NULL) return NULL;
	
	abb_t* abb = abb_crear(cmp, destruir_dato);
	if (abb == NULL)
	{
		free(abb_disk);
		return NULL;
	}
	
	abb_disk->abb = abb;
	abb_disk->cantidad_archivos = 0;
	abb_disk->tamanio_ocupado = 0;
	
	return abb_disk;
}

void abb_disk_destruir(abb_disk_t* abb_disk)
{
	abb_destruir(abb_disk->abb);
	free(abb_disk);
}

char* obtenerNombreArchivo(char* base, unsigned long numero)
{
	char numero_archivo[10];
	char *nombre_archivo = malloc(sizeof(char)*255);
	
	sprintf(numero_archivo, "%lu", numero);
	strcpy(nombre_archivo, base);
	strcat(nombre_archivo, numero_archivo);
	strcat(nombre_archivo, ".tmp");	
	
	return nombre_archivo;
}

char* obtenerNombreArchivoAbb(unsigned long numero)
{
	return obtenerNombreArchivo(BaseNombreArchivo, numero);
}

char* obtenerNombreArchivoAbbMerged(unsigned long numero)
{
	return obtenerNombreArchivo(BaseNombreArchivoMerged, numero);
}

void escribirAbbADisco(abb_disk_t* abb_disk)
{
	char *nombreArchivo = obtenerNombreArchivoAbb(abb_disk->cantidad_archivos);
	
	FILE* fd = fopen(nombreArchivo, "w");
	free(nombreArchivo);
	
	abb_iter_t* iter = abb_iter_in_crear(abb_disk->abb);
	const char* listaPalabrasClave;
	lista_t* listaPalabras;
	lista_iter_t* iteradorLista;
	apariciones_t* apariciones;

		
	while(abb_iter_in_al_final(iter) == false)
	{	
		listaPalabrasClave = abb_iter_in_ver_actual(iter);
		listaPalabras = abb_obtener(abb_disk->abb, listaPalabrasClave);
		
		iteradorLista = lista_iter_crear(listaPalabras);
		while (lista_iter_al_final(iteradorLista) == false)
		{
			apariciones = lista_iter_ver_actual(iteradorLista);
				
			// Si es de un termino va de una, si es de dos se fija de que tenga más de una aparicion
			if ( strExisteCadena(listaPalabrasClave, ' ') == false || apariciones->valor > 1 )
			{
				if (apariciones->valor > 0) fprintf(fd, "%s%c%lu%c%lu\n", listaPalabrasClave, 
				CaracterSeparador, apariciones->valor, CaracterSeparador, apariciones->documentoIndex);
			}
			
			lista_iter_avanzar(iteradorLista);
		}
		
		lista_iter_destruir(iteradorLista);
		lista_destruir(listaPalabras, free);
		abb_iter_in_avanzar(iter);
	}
	
	abb_iter_in_destruir(iter);
	fclose(fd);
	
	abb_destruir(abb_disk->abb);
	// HACER GENERICA LA LINEA DE ABAJO!
	abb_disk->abb = abb_crear(strcmp, NULL);
}

elementoAbbMerge_t* leerElementoArchivo(FILE* fd)
{
	char palabra[MaxCaracteresPorPalabra];
	char* palabraMem;
	bool agregarCaracter = true;
	unsigned long index = 0;
	int estado = 0;		// 0 Palabra, 1 Valor, 2 Documento
	elementoAbbMerge_t* elemento = malloc(sizeof(elementoAbbMerge_t));
	if (elemento == NULL) return NULL;
	
	char c = fgetc(fd);
	if (c == EOF)
	{
		free(elemento);
		return NULL;
	}
	
	while(1)
	{
		if (c == CaracterSeparador || c == '\n') agregarCaracter = false;
		
		if (agregarCaracter == false)
		{
			palabra[index] = '\0';
			if (estado == 0)
			{
				palabraMem = malloc(sizeof(char)*(1 + strlen(palabra)));
				strcpy(palabraMem, palabra);
				elemento->palabra = palabraMem;
				estado++;
			} else if (estado == 1)
			{
				elemento->valor = (unsigned long) atol(palabra);
					estado++;
			} else if (estado == 2)
			{
				elemento->documentoIndex = (unsigned long) atol(palabra);
				estado++;
			}			
			agregarCaracter = true;
			index = 0;
		} else {
			palabra[index] = c;
			index++;
		}
		
		if (c == '\n') break;
		c = fgetc ( fd ); 
	}

	return elemento;
}

void mergearArchivos(unsigned long start, unsigned long end, char* baseStart, char* baseEnd,
unsigned int mergedIndex)
{
	char* nombreArchivo;
	FILE *fdsAMergear[end - start + 1];
	FILE *fdMergeado;
	heap_t* elementos = heap_crear((cmp_func_t) compElementoAbbMerge);
	elementoAbbMerge_t* elementoAbb;
	elementoAbbMerge_t* elementoAbbTemp;

	for (unsigned long i = start; i < end; i++)
	{
		nombreArchivo = obtenerNombreArchivo(baseStart, i);
		fdsAMergear[i - start] = fopen(nombreArchivo, "r");
		free(nombreArchivo);
		
		elementoAbb = leerElementoArchivo(fdsAMergear[i - start]);
		elementoAbb->archivoNumero = i - start;
		
		heap_encolar(elementos, elementoAbb);		
	}
	
	nombreArchivo = obtenerNombreArchivo(baseEnd, mergedIndex);
	fdMergeado = fopen(nombreArchivo, "w");
	free(nombreArchivo);
	
	while(heap_esta_vacio(elementos) == false)
	{
		elementoAbb = heap_desencolar(elementos);
		
		fprintf(fdMergeado, "%s%c%lu%c%lu\n", elementoAbb->palabra, CaracterSeparador, 
		elementoAbb->valor, CaracterSeparador, elementoAbb->documentoIndex);
		
		elementoAbbTemp = leerElementoArchivo(fdsAMergear[ elementoAbb->archivoNumero ]);
		if (elementoAbbTemp != NULL)
		{
			elementoAbbTemp->archivoNumero = elementoAbb->archivoNumero;
			heap_encolar(elementos, elementoAbbTemp);
		}
		
		elementoAbbMerge_destruir(elementoAbb);
	}
	
	for (unsigned int j = start; j < end; j++)
	{
		fclose(fdsAMergear[j - start]);
		nombreArchivo = obtenerNombreArchivo(baseStart, j);
		remove(nombreArchivo);	// Se eliminan los archivos que ya se mergearon
		free(nombreArchivo);
	}
	
	fclose(fdMergeado);
	heap_destruir(elementos, NULL);
}

void abb_disk_merge(abb_disk_t* abb_disk)
{
	int mergeBy = sqrt( abb_disk->cantidad_archivos );
	
	if (mergeBy == 1)
	{
		mergearArchivos(0, abb_disk->cantidad_archivos, BaseNombreArchivo, BaseNombreArchivoAllMerged, 0);	
	} else {
		unsigned long mergeadoHasta;
		
		for (unsigned long primero = 0; primero + mergeBy < abb_disk->cantidad_archivos; primero = primero + mergeBy)
		{	
			mergearArchivos(primero, primero + mergeBy, BaseNombreArchivo, BaseNombreArchivoMerged, primero/mergeBy);	
			mergeadoHasta = primero + mergeBy;
		}
		
		if (ceil(sqrt( abb_disk->cantidad_archivos )) - mergeBy == 1)
		{	// Quiere decir que sobran archivos sin mergear
			mergearArchivos(mergeadoHasta, abb_disk->cantidad_archivos , BaseNombreArchivo, BaseNombreArchivoMerged, mergeBy);
		}
		
		mergearArchivos(0, ceil(sqrt( abb_disk->cantidad_archivos )) , BaseNombreArchivoMerged, BaseNombreArchivoAllMerged, 0);	
	}
	
}

void abb_disk_bajar_al_disco(abb_disk_t* abb_disk)
{
	if (abb_cantidad(abb_disk->abb) > 0)
	{
		escribirAbbADisco(abb_disk);
		abb_disk->cantidad_archivos++;
		abb_disk->tamanio_ocupado = 0;
	}
}

void abb_disk_sumar_tamanio(abb_disk_t* abb_disk, size_t tamanio)
// Se debe sumar el tamanio luego de agregar los elementos
{
	abb_disk->tamanio_ocupado = abb_disk->tamanio_ocupado + tamanio;
	
	if (abb_disk->tamanio_ocupado > MemoriaMaxima)
	{
		abb_disk_bajar_al_disco(abb_disk);
	}
}

void strLower(char str[])
{
	for (int i = 0; str[i] != '\0'; i++)
	{
		str[i] = (char)tolower(str[i]);
	}
}

bool esArchivoValido(char* archivoNombre)
{
	if (archivoNombre[ strlen(archivoNombre) -1] == '~') return false;
	//if (fileName[ strlen(fileName) -6] != '_' || fileName[ strlen(fileName) -5] != 'p') return false;
	return true;
}

bool esPalabraValida(char* palabra)
{
	bool admitido;
	bool prevAdmitido = false; // Si hay dos caracteres permitidos seguidos se 
							   // considera no admitida la palabra
	
	for (int i = 0; i < strlen(palabra); i++)
	{
		admitido = ( isalnum(palabra[i]) == 8);
		
		for (int j = 0; j < CaracteresAdmitidosPalabraLength; j++)
		{
			if (palabra[i] == CaracteresAdmitidosPalabra[j]) 
			{
				if (prevAdmitido == false) 
				{
					prevAdmitido = true;
				} else {
					admitido = false;
					break;
				}
				
				admitido = true;
			}
		}
		
		if (admitido == false) return false;
	}
	
	return true;
}

lista_t* obtenerNombresArchivos(void)
{
	lista_t* archivosNombres = lista_crear();

	FILE* archivoNombresArchivo;
	archivoNombresArchivo = fopen(NombreArchivoArchivos, "w");

	DIR *dir;
	struct dirent *dent;
	dir = opendir(pathCarpetaDatos);
	char* pathArchivo ;

	if(dir != NULL)
	{
		while( (dent=readdir(dir)) != NULL )
		{
			if (strlen(dent->d_name) > 4 && esArchivoValido(dent->d_name))
			{
				pathArchivo = malloc(sizeof(char)*(strlen(pathCarpetaDatos) + strlen(dent->d_name))+ sizeof(char));
				strcpy(pathArchivo, pathCarpetaDatos);
				strcat(pathArchivo, dent->d_name);
				lista_insertar_ultimo(archivosNombres, pathArchivo);
				fprintf(archivoNombresArchivo, "%s\n", pathArchivo);
			}
		}
	}

	closedir(dir);
	fclose(archivoNombresArchivo);

	return archivosNombres;
}

void agregarAparicion(abb_disk_t* elementosMatriz, char* palabra, unsigned int documentoIndex)
// Se tiene un arbol que tiene como claves a las palabras y como valor una lista, esa
// lista tiene elementos del tipo appearences_t que guardan el documento y la cantidad
// de apariciones de esa palabra en el documento.
{
	size_t tamanioAgregado = 0;
	
	lista_t* wordVector;
	apariciones_t* apariciones;
	
	// Si la palabra no existe en el arbol la agrego y le pongo una lista
	if (abb_pertenece(elementosMatriz->abb, palabra))
	{
		wordVector = abb_obtener(elementosMatriz->abb, palabra);
	} else {
		wordVector = lista_crear();
		abb_guardar(elementosMatriz->abb, palabra, wordVector);
		
		tamanioAgregado = tamanioAgregado + sizeof(char)*strlen(palabra);
	}
	
	apariciones = lista_ver_ultimo(wordVector);
	// Si dicho documento no aparece en la lista, lo creo y agrego
	if (apariciones == NULL || apariciones->documentoIndex != documentoIndex)
	{
		apariciones = malloc(sizeof(apariciones_t));
		apariciones->valor = 0;
		apariciones->documentoIndex = documentoIndex;
		lista_insertar_ultimo(wordVector, apariciones);	
		
		tamanioAgregado = tamanioAgregado + sizeof(apariciones_t);
	}
	
	apariciones->valor++;
	
	abb_disk_sumar_tamanio(elementosMatriz, tamanioAgregado);
}

void obtenerElementosArchivo(abb_disk_t* elementosMatriz, unsigned int documentoIndex, char* archivoNombre)
{
	FILE * fd;
	fd = fopen(archivoNombre, "r");
	unsigned long index = 0;
	
	bool agregarCaracter = true;
	char word[MaxCaracteresPorPalabra];
	char prevWord[MaxCaracteresPorPalabra*2 + 1];
	strcpy(prevWord, "");
	
	// Para agregar palabras dobles se espera que estas esten separadas por un espacio
	bool considerarPalabrasJuntasPrev = false;
	bool considerarPalabrasJuntas = false;
	char ch = fgetc ( fd ); 
	while(ch != EOF)
	{
		for (int i = 0; i < CaracteresSeparadoresLength; i++)
		{
			if (ch == caracteresSeparadores[i])
			{
				agregarCaracter = false;
				
				considerarPalabrasJuntas = considerarPalabrasJuntasPrev;
				
				if (ch == ' ') 
					considerarPalabrasJuntasPrev = true;
				else
					considerarPalabrasJuntasPrev = false;
							
				
				break;
			}
		}
		
		if (agregarCaracter == false)
		{
			word[index] = '\0';
			if ( strlen(word) > 0 )
			{
				strLower(word);
				
				if (esPalabraValida(word)) 
				{
					agregarAparicion(elementosMatriz, word, documentoIndex);
					
					if (considerarPalabrasJuntas)
					{
						strcat(prevWord, " ");
						strcat(prevWord, word);
						agregarAparicion(elementosMatriz, prevWord, documentoIndex);
					}
					 
					 strcpy(prevWord, word);
				}
			}
			agregarCaracter = true;
			index = 0;
		} else {
			if (index < MaxCaracteresPorPalabra)
			{
				word[index] = ch;
				index++;
			} else {
				// Si la palabra es demasiado larga es invalida y se saltea
				while (ch != ' ' && ch != EOF) ch = fgetc ( fd );
				index = 0;
			}
		}
		
		if (ch == EOF) break;
		ch = fgetc ( fd ); 
	}
	
	fclose(fd);
	
	printf("Terminado con archivo N°%u\t%s\n", documentoIndex + 1, archivoNombre);
}

abb_disk_t* obtenerTodosLosElementos(lista_t* archivosNombres)
{
	abb_disk_t* elementosMatriz = abb_disk_crear(strcmp, NULL);
	
	unsigned int i = 0;
	lista_iter_t* iter = lista_iter_crear(archivosNombres);
	while (lista_iter_al_final(iter) == false)
	{
		obtenerElementosArchivo(elementosMatriz, i, lista_iter_ver_actual(iter));
		lista_iter_avanzar(iter);
		i++;
	}
	
	lista_iter_destruir(iter);
	
	return elementosMatriz;
}

size_t guardarListaPalabras()
{
        char* nombreArchivo = obtenerNombreArchivo(BaseNombreArchivoAllMerged, 0);
        FILE* fd = fopen(nombreArchivo, "r");
        free(nombreArchivo);

        FILE* fdListaPalabras = fopen(NombreArchivoPalabras, "w");

        char ultimaPalabra[MaxCaracteresPorPalabra] = "";
        size_t cantidadPalabras = 0;

        elementoAbbMerge_t* elemento = leerElementoArchivo(fd);
        while(elemento != NULL)
        {
                if ( strcmp(ultimaPalabra, elemento->palabra) != 0 )
                {
                        strcpy(ultimaPalabra, elemento->palabra);
                        fprintf(fdListaPalabras, "%s\n", ultimaPalabra);
                        cantidadPalabras++;	
                }

                elementoAbbMerge_destruir(elemento);

                elemento = leerElementoArchivo(fd);
        }

        fclose(fdListaPalabras);
        fclose(fd);

        return cantidadPalabras;
}

unsigned long obtenerNnz()
{
	char* nombreArchivo = obtenerNombreArchivo(BaseNombreArchivoAllMerged, 0);
	FILE* fd = fopen(nombreArchivo, "r");
	free(nombreArchivo);

	unsigned long nnz = 1;

	bool cond;
	elementoAbbMerge_t* elementoPrev = leerElementoArchivo(fd);
	elementoAbbMerge_t* elemento = leerElementoArchivo(fd);
	while(elemento != NULL)
	{
		cond = (strcmp(elementoPrev->palabra, elemento->palabra) == 0) && (elementoPrev->documentoIndex == elemento->documentoIndex);
		while (cond)
		{
			// El elemento quedo repartido en dos archivos mergeados
			elementoPrev->valor = elementoPrev->valor + elemento->valor;
			elementoAbbMerge_destruir(elemento);
			elemento = leerElementoArchivo(fd);
			cond = (strcmp(elementoPrev->palabra, elemento->palabra) == 0) && (elementoPrev->documentoIndex == elemento->documentoIndex);
		}
		nnz++;
		elementoAbbMerge_destruir(elementoPrev);
		elementoPrev = elemento;
		elemento = leerElementoArchivo(fd);
	}
	
	elementoAbbMerge_destruir(elementoPrev);
	
	fclose(fd);
	
	return nnz;
}

void guardarMatrizEnDisco(unsigned long nnz, unsigned int cantidadDocumentos, size_t cantidadPalabras)
{
	FILE * archivoMatriz;
	archivoMatriz = fopen(NombreMatriz, "w");
	
	// Header
	fprintf(archivoMatriz, "# name: M\n# type: sparse matrix\n");
	fprintf(archivoMatriz, "# nnz: %lu\n", nnz);
	fprintf(archivoMatriz, "# rows: %u\n", cantidadDocumentos);
	fprintf(archivoMatriz, "# columns: %zu\n", cantidadPalabras);

	char* nombreArchivo = obtenerNombreArchivo(BaseNombreArchivoAllMerged, 0);
	FILE* fd = fopen(nombreArchivo, "r");
	free(nombreArchivo);

	char ultimaPalabra[MaxCaracteresPorPalabra] = "";
	unsigned long columna = 0;
	
	bool cond;
	elementoAbbMerge_t* elementoPrev = leerElementoArchivo(fd);
	elementoAbbMerge_t* elemento = leerElementoArchivo(fd);
	while(elemento != NULL)
	{
		cond = (strcmp(elementoPrev->palabra, elemento->palabra) == 0) && (elementoPrev->documentoIndex == elemento->documentoIndex);
		while (cond)
		{
			// El elemento quedo repartido en dos archivos mergeados
			elementoPrev->valor = elementoPrev->valor + elemento->valor;
			elementoAbbMerge_destruir(elemento);
			elemento = leerElementoArchivo(fd);
			cond = (strcmp(elementoPrev->palabra, elemento->palabra) == 0) && (elementoPrev->documentoIndex == elemento->documentoIndex);
		}

		if ( strcmp(ultimaPalabra, elementoPrev->palabra) != 0 )
		{
			strcpy(ultimaPalabra, elementoPrev->palabra);
			columna++;
		}
		
		// El formato es fila columna valor
		fprintf(archivoMatriz, "%lu %lu %lu\n", elementoPrev->documentoIndex + 1, columna, elementoPrev->valor);
		
		elementoAbbMerge_destruir(elementoPrev);
		elementoPrev = elemento;
		elemento = leerElementoArchivo(fd);
	}	
	
	if ( strcmp(ultimaPalabra, elementoPrev->palabra) != 0 )
	{
		strcpy(ultimaPalabra, elementoPrev->palabra);
		columna++;
	}
		
	// El formato es fila columna valor
	fprintf(archivoMatriz, "%lu %lu %lu\n", elementoPrev->documentoIndex + 1, columna, elementoPrev->valor);
	
	elementoAbbMerge_destruir(elementoPrev);
	
	fclose(fd);
	fclose(archivoMatriz);
}

void guardarMatrizPonderada(unsigned long nnz, unsigned int cantidadDocumentos, size_t cantidadPalabras)
{
	char pathArchivo[255];
	sprintf(pathArchivo, "./%s/%s", repositorioNombre, NombreMatrizPonderada);
	
	FILE * matrizPonderada = fopen(pathArchivo, "w");

	// Header
	fprintf(matrizPonderada, "# name: P\n# type: sparse matrix\n");
	fprintf(matrizPonderada, "# nnz: %lu\n", nnz);
	fprintf(matrizPonderada, "# rows: %u\n", cantidadDocumentos);
	fprintf(matrizPonderada, "# columns: %zu\n", cantidadPalabras);

	FILE* fd = fopen(NombreMatriz, "r");

	saltarLineasArchivo(5,fd); // Salteo las primeras 5 lineas del archivo

	bool cond;
	bool finArchivo = false;
	unsigned int totalOcurrencias = 0;
	lista_t* aparicionesPalabra = NULL;
	elementoMatriz_t* elementoPrev = leerElementoMatriz(fd);
	elementoMatriz_t* elemento = leerElementoMatriz(fd);

	while(elemento != NULL)
	{
		aparicionesPalabra = lista_crear();
		lista_insertar_ultimo(aparicionesPalabra, elementoPrev);
		totalOcurrencias += elementoPrev->valor;
		// Si los elementos son la misma palabra, cond es true
		cond = (elementoPrev->columna == elemento->columna);
		while (cond)
		{
			// Agrego el ultimo elemento leido a la lista
			lista_insertar_ultimo(aparicionesPalabra, elemento);
			totalOcurrencias += elemento->valor;
			elemento = leerElementoMatriz(fd);
			if (elemento != NULL) cond = (elementoPrev->columna == elemento->columna);
			else {
				cond = false;
				finArchivo = true;
			}
		}

		metodoLogEntropia(aparicionesPalabra, cantidadDocumentos, totalOcurrencias);
		//metodoTFIDF(aparicionesPalabra, cantidadDocumentos);

		lista_iter_t* iter = lista_iter_crear(aparicionesPalabra);
		while ( ! lista_iter_al_final(iter))
		{
			// El formato es fila columna valor
			elementoMatriz_t* unElemento = lista_iter_ver_actual(iter);
			fprintf(matrizPonderada, "%lu %lu %f\n", unElemento->fila, unElemento->columna, unElemento->valor);
			lista_iter_avanzar(iter);
		}

		lista_iter_destruir(iter);
		lista_destruir(aparicionesPalabra,free);
		totalOcurrencias = 0;
		if (! finArchivo){
			elementoPrev = elemento;
			elemento = leerElementoMatriz(fd);
		}

	}

	if (! finArchivo){
		// Si no estoy en fin de archivo es porque el ultimo elemento tiene una sola aparicion
		aparicionesPalabra = lista_crear();
		lista_insertar_ultimo(aparicionesPalabra, elementoPrev);
		totalOcurrencias += elementoPrev->valor;

		metodoLogEntropia(aparicionesPalabra, cantidadDocumentos, totalOcurrencias);
		//metodoTFIDF(aparicionesPalabra, cantidadDocumentos);

		lista_iter_t* iter = lista_iter_crear(aparicionesPalabra);
		while ( ! lista_iter_al_final(iter))
		{
			// El formato es fila columna valor
			elementoMatriz_t* unElemento = lista_iter_ver_actual(iter);
			fprintf(matrizPonderada, "%lu %lu %f\n", unElemento->fila, unElemento->columna, unElemento->valor);
			lista_iter_avanzar(iter);
		}

		lista_iter_destruir(iter);
		lista_destruir(aparicionesPalabra,free);
	}

	fclose(fd);
	fclose(matrizPonderada);
}

void computeSVD(void)
{
	char command[120];
	printf("Computing SVD...\n");
	sprintf(command, "octave -q --eval 'svdSave(%d, \"%s\")'", rank, repositorioNombre);
	system(command);
	printf("SVD completed!\n");
}

double timeval_diff(struct timeval *a, struct timeval *b)
{
	return (double)(a->tv_sec + (double)a->tv_usec/1000000) -
		(double)(b->tv_sec + (double)b->tv_usec/1000000);
}

bool guardarParametros(int argc, char *argv[])
{
	if (argc < 4) 
	{
		printf("El formato para pasar los parametros al programa es INCORRECTO!\n\n"
		"Primero se debe pasar nombre del repositorio a crear, luego el tamaño de"
		" la matriz recortada precedido de un guión y, al final, la carpeta donde "
		"estan los archivos a parsear.\n\nEj: Tpgrupo17i libros -200 /home/TP/datos\n");
		
		return false;
	}
	repositorioNombre = argv[1];
	rank = atoi(argv[2] + sizeof(char)); // Salteo el primer caracter que es el guion.
	pathCarpetaDatos = argv[3];
	
	if (rank == 0) 
	{
		printf("El tamaño de la matriz debe ser un entero y se debe pasar con un guión"
		" precediendo el numero.\n\nEj: Tpgrupo17i libros -200 /home/TP/datos\n");
		return false;
	}
	
	return true;
}

void crearDirectorioRepositorio(void)
{
	char command[120];
	sprintf(command, "mkdir %s -p", repositorioNombre);
	system(command);
}

int main(int argc, char *argv[])
{
	int ok = guardarParametros(argc, argv);
	if (ok == false) return -1;
	
	crearDirectorioRepositorio();
	
	unsigned int cantidadDocumentos;
	size_t cantidadPalabras;
	unsigned long nnz;
	
	struct timeval t_ini, t_fin;
	double secs;
	
	gettimeofday(&t_ini, NULL);
	
	lista_t* filesNames = obtenerNombresArchivos();
	cantidadDocumentos = lista_largo(filesNames);

	abb_disk_t* matrixElements = obtenerTodosLosElementos(filesNames);
	abb_disk_bajar_al_disco(matrixElements);
	abb_disk_merge(matrixElements);
	cantidadPalabras = guardarListaPalabras();
	nnz = obtenerNnz();
	
	guardarMatrizEnDisco(nnz, cantidadDocumentos, cantidadPalabras);
	guardarMatrizPonderada(nnz, cantidadDocumentos, cantidadPalabras);
	
	gettimeofday(&t_fin, NULL);
	secs = timeval_diff(&t_fin, &t_ini);
	printf("Tiempo empleado: %gs\n",secs);
	
	computeSVD();
	
	abb_disk_destruir(matrixElements);	
	lista_destruir(filesNames, free);
	
	gettimeofday(&t_fin, NULL);
	secs = timeval_diff(&t_fin, &t_ini);
	printf("Tiempo empleado: %gs\n",secs);
		
	return 0;
}
